import {
  Injectable,
  Logger,
  NotFoundException,
  BadRequestException,
  ConflictException
} from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository, Like, Raw, IsNull } from 'typeorm'
import { File } from 'src/database/entities/file.entity'
import * as fs from 'fs-extra'
import * as path from 'path'

export interface FileViewModel {
  id: number
  filename: string
  fileHash: string
  size: number
  userId: number
  mimeType: string
  isPublic: boolean
  type: string
  parentId: number
  url: string
  createdAt: string
  updatedAt: string
  sizeFormatted: string
}

@Injectable()
export class FileService {
  private readonly logger = new Logger(FileService.name)
  private readonly uploadDir = path.join(process.cwd(), 'uploads')
  private readonly finalDir = path.join(this.uploadDir, 'final')

  constructor(
    @InjectRepository(File)
    private readonly fileRepository: Repository<File>
  ) {
    // 确保目录存在
    fs.ensureDirSync(this.finalDir)
  }

  /**
   * 获取指定用户的文件列表（分页，支持文件名模糊搜索）
   * @param userId - 用户ID
   * @param page - 页码，从1开始
   * @param size - 每页条数
   * @param filename - 文件名(可选，用于模糊搜索)
   * @param type - 文件类型(可选，筛选类型: 'all', 'music', 'video', 'doc', 'image', 'others')
   * @param parentId - 父级目录ID(可选，默认为null表示根目录)
   */
  async getUserFiles(
    userId: number,
    page: number = 1,
    size: number = 10,
    filename?: string,
    filetype?: string,
    parentId?: number
  ): Promise<{ data: FileViewModel[]; total: number }> {
    try {
      // 确保页码和大小是合理的值
      page = Math.max(1, page)
      size = Math.max(1, Math.min(100, size))

      // 计算跳过的记录数
      const skip = (page - 1) * size

      // 构建查询条件
      const whereCondition: any = { userId }

      parentId === undefined || parentId === null
        ? (whereCondition.parentId = IsNull()) // 查找parentId为null的记录
        : (whereCondition.parentId = parentId)

      // 如果提供了文件名，添加模糊搜索条件
      filename && (whereCondition.filename = Like(`%${filename}%`))

      // 如果选择了特定文件类型（非"all"），添加类型筛选条件
      if (filetype && filetype !== 'all')
        switch (filetype) {
          case 'music':
            whereCondition.mimeType = Like('music/%')
            break
          case 'video':
            whereCondition.mimeType = Like('video/%')
            break
          case 'doc':
            // 文档类型包括：PDF, Office文档等
            whereCondition.mimeType = Raw(
              (alias) => `(${alias} LIKE 'application/pdf' OR 
                                         ${alias} LIKE 'application/msword' OR 
                                         ${alias} LIKE 'application/vnd.openxmlformats-officedocument.%' OR
                                         ${alias} LIKE 'application/vnd.ms-%' OR
                                         ${alias} LIKE 'text/%')`
            )
            break
          case 'image':
            whereCondition.mimeType = Like('image/%')
            break
          case 'others':
            whereCondition.mimeType = Raw(
              (alias) => `(${alias} NOT LIKE 'music/%' AND
                                         ${alias} NOT LIKE 'video/%' AND
                                         ${alias} NOT LIKE 'image/%' AND
                                         ${alias} NOT LIKE 'application/pdf' AND
                                         ${alias} NOT LIKE 'application/msword' AND
                                         ${alias} NOT LIKE 'application/vnd.openxmlformats-officedocument.%' AND
                                         ${alias} NOT LIKE 'application/vnd.ms-%' AND
                                         ${alias} NOT LIKE 'text/%')`
            )
            break
        }

      // 查询符合条件的总记录数
      const total = await this.fileRepository.count({
        where: whereCondition
      })

      // 查询分页数据
      const files = await this.fileRepository.find({
        where: whereCondition,
        skip,
        take: size,
        order: { createdAt: 'DESC' } // 默认按创建时间倒序排列
      })

      // 检查文件是否实际存在
      const validFiles: FileViewModel[] = []
      for (const file of files) {
        if (file.type === 'folder' || (await fs.pathExists(file.filePath))) {
          validFiles.push(this.toViewModel(file))
        }
      }

      return {
        data: validFiles,
        total
      }
    } catch (error) {
      this.logger.error(`获取用户文件列表失败: ${error.message}`)
      throw error
    }
  }

  /**
   * 将日期格式化为 YYYY-MM-DD HH:mm:ss 格式
   * @param date - 需要格式化的日期
   */
  private formatDate(date: Date): string {
    if (!date) return ''

    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    const hours = String(date.getHours()).padStart(2, '0')
    const minutes = String(date.getMinutes()).padStart(2, '0')
    const seconds = String(date.getSeconds()).padStart(2, '0')

    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  }

  /**
   * 转换为视图模型
   * @param file - 文件实体
   */
  private toViewModel(file: File): FileViewModel {
    // 将文件大小转换为带单位的格式
    let formattedSize: string
    if (file.size < 1024) {
      formattedSize = `${file.size} B`
    } else if (file.size < 1024 * 1024) {
      formattedSize = `${(file.size / 1024).toFixed(2)} KB`
    } else if (file.size < 1024 * 1024 * 1024) {
      formattedSize = `${(file.size / (1024 * 1024)).toFixed(2)} MB`
    } else {
      formattedSize = `${(file.size / (1024 * 1024 * 1024)).toFixed(2)} GB`
    }

    return {
      ...file,
      sizeFormatted: formattedSize,
      url: `/uploads/final/${file.filename}`,
      createdAt: this.formatDate(file.createdAt),
      updatedAt: this.formatDate(file.updatedAt)
    }
  }

  /**
   * 获取文件详细信息
   * @param fileId - 文件ID
   * @param userId - 用户ID
   */
  async getFileDetails(
    fileId: number,
    userId: number
  ): Promise<File & { url: string }> {
    try {
      const file = await this.fileRepository.findOne({
        where: { id: fileId, userId }
      })

      if (!file) {
        throw new NotFoundException('文件不存在')
      }

      if (await fs.pathExists(file.filePath)) {
        return {
          ...file,
          url: `/uploads/final/${file.filename}`
        }
      }

      throw new NotFoundException('文件不存在')
    } catch (error) {
      this.logger.error(`获取文件详情失败: ${error.message}`)
      throw error
    }
  }

  /**
   * 删除文件或文件夹（递归删除）
   * @param fileId - 文件或文件夹ID
   * @param userId - 用户ID
   */
  async deleteFile(
    fileId: number,
    userId: number
  ): Promise<{ success: boolean }> {
    try {
      const file = await this.fileRepository.findOne({
        where: { id: fileId, userId }
      })

      if (!file) {
        throw new NotFoundException('文件不存在或无权限删除')
      }

      // 如果是文件夹，需要递归删除所有子项
      if (file.type === 'folder') {
        await this.recursiveDelete(file.id, userId)
      } else {
        // 删除物理文件
        if (await fs.pathExists(file.filePath)) {
          await fs.unlink(file.filePath)
          this.logger.log(`删除文件: ${file.filePath}`)
        }
      }

      // 删除数据库记录
      await this.fileRepository.remove(file)

      return { success: true }
    } catch (error) {
      this.logger.error(`删除文件失败: ${error.message}`)
      throw error
    }
  }

  /**
   * 递归删除文件夹及其所有内容
   * @param folderId - 文件夹ID
   * @param userId - 用户ID
   */
  private async recursiveDelete(
    folderId: number,
    userId: number
  ): Promise<void> {
    // 查找指定文件夹下的所有子项
    const children = await this.fileRepository.find({
      where: { parentId: folderId, userId }
    })

    // 递归删除所有子项
    for (const child of children) {
      if (child.type === 'folder') {
        // 如果子项是文件夹，递归删除
        await this.recursiveDelete(child.id, userId)
      } else {
        // 如果子项是文件，删除物理文件
        if (await fs.pathExists(child.filePath)) {
          await fs.unlink(child.filePath)
          this.logger.log(`删除文件: ${child.filePath}`)
        }
      }

      // 删除数据库记录
      await this.fileRepository.remove(child)
    }
  }

  /**
   * 重命名文件
   * @param fileId - 文件ID
   * @param newFilename - 新文件名
   * @param userId - 用户ID
   */
  async renameFile(
    fileId: number,
    newFilename: string,
    userId: number
  ): Promise<File & { url: string }> {
    try {
      const file = await this.fileRepository.findOne({
        where: { id: fileId, userId }
      })

      if (!file) {
        throw new NotFoundException('文件不存在或无权限修改')
      }

      // 更新文件名
      const fileExtension = path.extname(file.filename)
      const baseFilename = newFilename.endsWith(fileExtension)
        ? newFilename
        : `${newFilename}${fileExtension}`

      const newFilePath = path.join(this.finalDir, baseFilename)

      // 如果文件名已更改，则重命名物理文件
      if (
        file.filename !== baseFilename &&
        (await fs.pathExists(file.filePath))
      ) {
        await fs.rename(file.filePath, newFilePath)
      }

      file.filename = baseFilename
      file.filePath = newFilePath

      await this.fileRepository.save(file)

      return {
        ...file,
        url: `/uploads/final/${file.filename}`
      }
    } catch (error) {
      this.logger.error(`重命名文件失败: ${error.message}`)
      throw error
    }
  }

  /**
   * 创建新目录
   * @param name - 目录名称
   * @param userId - 用户ID
   * @param parentId - 父级目录ID
   */
  async createFolder(
    filename: string,
    userId: number,
    parentId?: number
  ): Promise<File> {
    try {
      // 检查父级目录是否存在（如果指定了父级目录）
      if (parentId) {
        const parentDir = await this.fileRepository.findOne({
          where: { id: parentId, userId }
        })

        if (!parentDir) {
          throw new NotFoundException('父级目录不存在')
        }

        // 验证父级是否为目录类型
        if (parentDir.type !== 'folder') {
          throw new BadRequestException('指定的父级不是一个目录')
        }
      }

      // 检查同一父级目录下是否已存在同名目录
      const existingDir = await this.fileRepository.findOne({
        where: {
          filename,
          parentId,
          userId,
          mimeType: 'folder'
        }
      })

      if (existingDir) {
        throw new ConflictException('同一目录下已存在同名文件夹')
      }

      // 创建目录实体
      const folder = this.fileRepository.create({
        filename,
        fileHash: `dir_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, // 生成唯一标识
        filePath: '',
        size: 0,
        userId,
        type: 'folder',
        isPublic: false,
        parentId
      })

      // 保存到数据库
      await this.fileRepository.save(folder)
      this.logger.log(`用户${userId}创建了新目录: ${filename}`)

      return folder
    } catch (error) {
      this.logger.error(`创建目录失败: ${error.message}`)
      throw error
    }
  }
}
